<template>
  <div ref="contentRef" class="markdown-view" v-html="contentHtml"></div>
</template>

<script setup>
import { ElMessage } from "element-plus";
import { useClipboard } from "@vueuse/core";
import { marked } from "marked";
import "highlight.js/styles/vs2015.min.css";
import hljs from "highlight.js";

// 定义组件属性
const props = defineProps({
  content: {
    type: String,
    required: true,
  },
});

const { copy } = useClipboard(); // 初始化 copy 到粘贴板
const contentRef = ref();
const contentHtml = ref(); // 渲染的html内容
const { content } = toRefs(props); // 将 props 变为引用类型

// 流式渲染优化
let renderTimer = null;
let lastRenderTime = 0;
const RENDER_DEBOUNCE_DELAY = 16; // 约60fps

/** marked 渲染器 */
const renderer = {
  code(code, language) {
    let actualCode = code;
    let actualLanguage = language;
    // 检查 code 是否为对象
    if (typeof code === "object" && code !== null && code.type === "code") {
      actualCode = code.text;
      actualLanguage = code.lang;
    }
    let highlightHtml;
    try {
      highlightHtml = hljs.highlight(actualCode, {
        language: actualLanguage ? actualLanguage : "plaintext",
        ignoreIllegals: true,
      }).value;
    } catch (e) {
      // skip
    }
    const copyHtml = `<div id="copy" data-copy='${actualCode}' style="position: absolute; right: 10px; top: 5px; color: #fff;cursor: pointer;">复制</div>`;
    return `<pre style="position: relative;">${copyHtml}<code class="hljs">${highlightHtml}</code></pre>`;
  },
  link(href) {
    // 创建一个带有点击事件的链接
    const linkHtml = `<a class="link_a" href="${href.href}" title="${
      href.title || ""
      }" target="_blank" rel="noopener noreferrer">${href.text}</a>`;
    return linkHtml;
  },
};

// 配置 marked
marked.use({
  renderer: renderer, // 自定义渲染器
  useNewRenderer: true, // 使用新版本的渲染器
  gfm: true, // 启用GFM语法(表格需要此配置)
  tables: true, // 显式启用表格支持
  breaks: false, // 禁用行内换行符
  pedantic: false, // 禁用严格模式
  sanitize: false, // 禁用HTML转义，允许原始HTML
});

/** 监听 content 变化 */
watch(content, async (newValue, oldValue) => {
  await debouncedRender(newValue, oldValue);
});

/**
 * 防抖渲染，优化流式输出性能
 * @param {string} newValue 
 * @param {string} oldValue 
 */
const debouncedRender = async (newValue, oldValue) => {
  const now = Date.now();
  
  // 清除之前的定时器
  if (renderTimer) {
    clearTimeout(renderTimer);
  }
  
  // 如果距离上次渲染时间太短，使用防抖
  if (now - lastRenderTime < RENDER_DEBOUNCE_DELAY) {
    renderTimer = setTimeout(async () => {
      await renderMarkdownWithStreaming(newValue, oldValue);
      lastRenderTime = Date.now();
    }, RENDER_DEBOUNCE_DELAY);
  } else {
    // 立即渲染
    await renderMarkdownWithStreaming(newValue, oldValue);
    lastRenderTime = now;
  }
};

/** 保护代码块中的数学公式 */
const processMathContent = (content) => {
  if (!content) return content;

  // 保护代码块中的数学公式，避免被MathJax处理
  const codeBlocks = [];
  let codeBlockIndex = 0;

  // 保存代码块
  content = content.replace(/```[\s\S]*?```/g, (match) => {
    codeBlocks.push(match);
    return `__CODE_BLOCK_${codeBlockIndex++}__`;
  });

  // 恢复代码块
  codeBlocks.forEach((block, index) => {
    content = content.replace(`__CODE_BLOCK_${index}__`, block);
  });

  return content;
};

/** 渲染 markdown */
const renderMarkdown = async (content) => {
  if (!content) {
    contentHtml.value = "";
    return;
  }

  // 保护代码块中的数学公式，避免被MathJax处理
  const processedContent = processMathContent(content);

  // 渲染 markdown
  const renderedHtml = await marked(processedContent);
  
  contentHtml.value = renderedHtml;

  // 等待 DOM 更新后渲染 MathJax
  await nextTick();

  // 确保 MathJax 已加载并渲染数学公式
  if (window.MathJax && window.MathJax.typesetPromise) {
    try {
      await window.MathJax.typesetPromise();
    } catch (error) {
      console.warn("MathJax rendering error:", error);
    }
  }
};

/**
 * 渲染 markdown 为 HTML 字符串（不直接修改 DOM）
 * @param {string} content
 * @returns {Promise<string>} 渲染后的 HTML 字符串
 */
const renderMarkdownToHtml = async (content) => {
  if (!content) return "";

  const processedContent = processMathContent(content);
  return await marked(processedContent);
};

/**
 * 流式渲染 Markdown，支持实时追加文本和逐步生成
 * @param {string} newContent - 新内容
 * @param {string} oldContent - 旧内容
 */
const renderMarkdownWithStreaming = async (newContent, oldContent) => {
  if (!newContent) {
    contentHtml.value = "";
    return;
  }

  // 如果内容没有变化，直接返回
  if (newContent === oldContent) {
    return;
  }

  // 检测是否为流式追加
  if (isStreamingAppend(newContent, oldContent)) {
    await renderStreamingAppend(newContent, oldContent);
  } else {
    // 非流式更新，使用完整重渲染
    await renderMarkdown(newContent);
  }
};

/**
 * 检测是否为流式追加
 * @param {string} newContent
 * @param {string} oldContent
 * @returns {boolean}
 */
const isStreamingAppend = (newContent, oldContent) => {
  if (!oldContent) return false;

  // 检查新内容是否以旧内容开头（流式追加特征）
  if (!newContent.startsWith(oldContent)) return false;

  // 检查追加内容是否安全
  const appendedContent = newContent.slice(oldContent.length);

  // 如果追加内容为空，不是流式追加
  if (!appendedContent.trim()) return false;

  // 检查是否在代码块中间
  const codeBlockCount = (oldContent.match(/```/g) || []).length;
  if (codeBlockCount % 2 !== 0) {
    return false; // 在代码块中间，不安全
  }

  // 检查是否在表格、列表等结构化内容中
  const lines = oldContent.split('\n');
  const lastLine = lines[lines.length - 1];
  
  // 如果在表格行中
  if (lastLine.includes('|')) {
    return false;
  }
  
  // 如果在列表项中
  if (/^\s*[-*+]\s/.test(lastLine) || /^\s*\d+\.\s/.test(lastLine)) {
    return false;
  }
  
  // 如果在引用中
  if (/^\s*>\s/.test(lastLine)) {
    return false;
  }

  return true;
};

/**
 * 渲染流式追加内容
 * @param {string} newContent
 * @param {string} oldContent
 */
const renderStreamingAppend = async (newContent, oldContent) => {
  const appendedContent = newContent.slice(oldContent.length);

  if (!appendedContent.trim()) return;

  // 使用 DOM 精准更新，避免整块重渲染导致图片闪动/重载
  const newHtml = await renderMarkdownToHtml(newContent);

  // 如果容器还未挂载，回退为完整渲染
  if (!contentRef.value || !contentRef.value.firstChild) {
    await renderMarkdown(newContent);
  } else {
    const tempContainer = document.createElement("div");
    tempContainer.innerHTML = newHtml;
    // 保持根容器属性不变，防止覆盖 .markdown-view 等样式
    tempContainer.className = contentRef.value.className;
    tempContainer.id = contentRef.value.id;
    tempContainer.style.cssText = contentRef.value.style.cssText;
    // 仅对差异部分做更新，保留已存在的 <img> 节点，避免重载
    streamingDOMUpdate(contentRef.value, tempContainer);
  }

  // 等待 DOM 更新后渲染 MathJax
  await nextTick();

  // 只对新添加的内容进行 MathJax 渲染
  if (window.MathJax && window.MathJax.typesetPromise) {
    try {
      await window.MathJax.typesetPromise();
    } catch (error) {
      console.warn("MathJax rendering error:", error);
    }
  }
};

/**
 * 精准追加内容到 DOM，避免完整重渲染
 * @param {HTMLElement} container - 容器元素
 * @param {string} newHtml - 新的 HTML 内容
 */
const appendToDOM = async (container, newHtml) => {
  // 创建临时容器来解析新 HTML
  const tempContainer = document.createElement("div");
  tempContainer.innerHTML = newHtml;

  // 获取新内容的子节点
  const newNodes = Array.from(tempContainer.childNodes);

  // 将新节点添加到现有容器中
  newNodes.forEach((node) => {
    const clonedNode = node.cloneNode(true);
    container.appendChild(clonedNode);
  });

  // 同步更新 contentHtml 以保持一致性
  contentHtml.value += newHtml;
};


/**
 * 精准更新 DOM，避免使用 innerHTML
 * @param {HTMLElement} container - 容器元素
 * @param {string} newHtml - 新的 HTML 内容
 */
const updateDOMWithPrecision = async (container, newHtml) => {
  // 创建临时容器来解析新 HTML
  const tempContainer = document.createElement("div");
  tempContainer.innerHTML = newHtml;

  // 获取新内容的子节点
  const newNodes = Array.from(tempContainer.childNodes);

  // 将新节点添加到现有容器中
  newNodes.forEach((node) => {
    // 克隆节点以避免从临时容器中移除
    const clonedNode = node.cloneNode(true);
    container.appendChild(clonedNode);
  });
};

/**
 * 流式渲染的精准 DOM 比较和更新
 * @param {HTMLElement} targetNode - 目标节点（当前 DOM）
 * @param {HTMLElement} sourceNode - 源节点（新的 DOM）
 */
function streamingDOMUpdate(targetNode, sourceNode) {
  if (!targetNode || !sourceNode) {
    console.warn("streamingDOMUpdate: 无效的节点参数");
    return;
  }

  /**
   * 流式渲染专用的节点比较和更新
   * @param {Node} node1 - 当前节点
   * @param {Node} node2 - 新节点
   */
  function streamingCompareAndUpdate(node1, node2) {
    try {
      // 情况 1：处理文本节点
      if (handleStreamingTextNodes(node1, node2)) return;

      // 情况 2：处理元素节点类型不匹配
      if (handleStreamingElementTypeMismatch(node1, node2)) return;

      // 情况 3：优先处理 IMG，避免闪动
      if (
        node1 &&
        node2 &&
        node1.nodeType === Node.ELEMENT_NODE &&
        node2.nodeType === Node.ELEMENT_NODE &&
        node1.tagName === 'IMG' &&
        node2.tagName === 'IMG'
      ) {
        updateStreamingImageAttributes(node1, node2);
        return;
      }

      // 情况 4：更新元素属性
      updateStreamingElementAttributes(node1, node2);

      // 情况 5：递归处理子节点
      updateStreamingChildNodes(node1, node2);
    } catch (error) {
      console.error("流式 DOM 更新过程中发生错误:", error);
      // 发生错误时回退到完整替换
      if (node1 && node1.parentNode) {
        const newNode = node2.cloneNode(true);
        node1.parentNode.replaceChild(newNode, node1);
      }
    }
  }

  /**
   * 处理流式渲染的文本节点
   * @param {Node} node1
   * @param {Node} node2
   * @returns {boolean} 是否已处理
   */
  function handleStreamingTextNodes(node1, node2) {
    if (
      node1 &&
      node1.nodeType === Node.TEXT_NODE &&
      node2.nodeType === Node.TEXT_NODE
    ) {
      if (node1.nodeValue !== node2.nodeValue) {
        node1.nodeValue = node2.nodeValue;
      }
      return true;
    }
    return false;
  }

  /**
   * 处理流式渲染的元素类型不匹配
   * @param {Node} node1
   * @param {Node} node2
   * @returns {boolean} 是否已处理
   */
  function handleStreamingElementTypeMismatch(node1, node2) {
    if (!node1 || node1.tagName !== node2.tagName) {
      const newNode = node2.cloneNode(true);
      if (node1 && node1.parentNode) {
        node1.parentNode.replaceChild(newNode, node1);
      } else if (node2.parentNode) {
        node2.parentNode.appendChild(newNode);
      }
      return true;
    }
    return false;
  }

  /**
   * 更新 IMG 的关键属性，避免因重复设置 src 导致重载
   * @param {HTMLImageElement} img1
   * @param {HTMLImageElement} img2
   */
  function updateStreamingImageAttributes(img1, img2) {
    // 仅当 src 实际变化时才更新
    const src1 = img1.getAttribute('src');
    const src2 = img2.getAttribute('src');
    if (src1 !== src2) {
      img1.setAttribute('src', src2 || '');
    }

    // 同步常用的非关键属性，但避免触发重新加载
    const attrsToSync = [
      'alt',
      'title',
      'width',
      'height',
      'referrerpolicy',
      'loading',
      'decoding'
    ];
    attrsToSync.forEach((attr) => {
      const v1 = img1.getAttribute(attr);
      const v2 = img2.getAttribute(attr);
      if (v1 !== v2) {
        if (v2 !== null) {
          img1.setAttribute(attr, v2);
        } else {
          img1.removeAttribute(attr);
        }
      }
    });

    // 同步通用样式/类名，但仅当不同
    if (img1.className !== img2.className) img1.className = img2.className;
    if (img1.style.cssText !== img2.style.cssText) {
      img1.style.cssText = img2.style.cssText;
    }
  }

  /**
   * 更新流式渲染的元素属性
   * @param {Element} node1
   * @param {Element} node2
   */
  function updateStreamingElementAttributes(node1, node2) {
    // 保护特殊节点不被修改
    const protectedAttributes = ["article-content"];

    // 更新 className
    if (
      !protectedAttributes.includes(node1.className) &&
      node1.className !== node2.className
    ) {
      node1.className = node2.className;
    }

    // 更新 id
    if (!protectedAttributes.includes(node1.id) && node1.id !== node2.id) {
      node1.id = node2.id;
    }

    // 更新 style
    if (node1.style.cssText !== node2.style.cssText) {
      node1.style.cssText = node2.style.cssText;
    }

    // 更新流式渲染相关的重要属性
    const importantAttributes = ["data-copy", "href", "title", "target", "rel"];
    importantAttributes.forEach((attr) => {
      if (node1.getAttribute(attr) !== node2.getAttribute(attr)) {
        if (node2.hasAttribute(attr)) {
          node1.setAttribute(attr, node2.getAttribute(attr));
        } else {
          node1.removeAttribute(attr);
        }
      }
    });
  }

  /**
   * 更新流式渲染的子节点
   * @param {Element} node1
   * @param {Element} node2
   */
  function updateStreamingChildNodes(node1, node2) {
    const children1 = Array.from(node1.childNodes);
    const children2 = Array.from(node2.childNodes);

    // 流式渲染优化：优先处理追加的节点
    let i = 0;
    const maxLength = Math.max(children1.length, children2.length);

    while (i < maxLength) {
      const child1 = children1[i];
      const child2 = children2[i];

      if (!child1 && child2) {
        // 新增节点（流式渲染常见情况）
        const newChild = child2.cloneNode(true);
        node1.appendChild(newChild);
        i++;
      } else if (child1 && !child2) {
        // 删除多余节点 - 添加安全检查
        if (child1.parentNode === node1) {
          node1.removeChild(child1);
          // 重新获取子节点数组，因为结构已改变
          children1.splice(i, 1);
          // 不增加 i，因为下一个元素现在在当前位置
        } else {
          i++;
        }
      } else if (child1 && child2) {
        // 递归比较和更新
        streamingCompareAndUpdate(child1, child2);
        i++;
      } else {
        i++;
      }
    }
  }

  // 开始流式比较和更新
  streamingCompareAndUpdate(targetNode, sourceNode);
}

/** 初始化 **/
onMounted(async () => {
  // 解析转换 markdown
  await renderMarkdown(props.content);
  // 添加 copy 监听
  contentRef.value.addEventListener("click", (e) => {
    if (e.target.id === "copy") {
      copy(e.target?.dataset?.copy);
      ElMessage.success("复制成功!");
    }
  });
});

/** 组件卸载时清理 */
onUnmounted(() => {
  // 清除渲染定时器
  if (renderTimer) {
    clearTimeout(renderTimer);
    renderTimer = null;
  }
});
</script>

<style lang="scss">
.markdown-view {
  line-height: 1.6;
  max-width: 100%;
  img {
    max-width: 100%;
    height: auto;
  }
  pre {
    position: relative;
    white-space: pre-wrap;
    word-wrap: break-word;
  }

  pre code.hljs {
    width: auto;
    white-space: pre-wrap;
    word-wrap: break-word;
  }

  .link_a {
    padding-left: 5px;
    font-weight: 500;
    color: rgb(58, 99, 142);

    &:hover {
      color: rgb(3, 118, 240);
    }
  }

  code.hljs {
    border-radius: 6px;
    padding-top: 20px;
    width: auto;

    @media screen and (min-width: 1536px) {
      width: 960px;
    }

    @media screen and (max-width: 1536px) and (min-width: 1024px) {
      width: calc(100vw - 400px - 64px - 32px * 2);
    }

    @media screen and (max-width: 1024px) and (min-width: 768px) {
      width: calc(100vw - 32px * 2);
    }

    @media screen and (max-width: 768px) {
      width: calc(100vw - 16px * 2);
    }
  }

  .math-container {
    margin: 6px 0;
    text-align: center;
  }

  .math-inline {
    display: inline;
  }

  code.hljs {
    margin-bottom: 16px;
  }

  p {
    word-break: break-all;
    word-wrap: break-word;
    margin: 3px 0;
  }

  /* 标题通用格式 */
  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    margin: 24px 0 8px;
    font-weight: 600;
  }

  h1 {
    font-size: 22px;
    line-height: 32px;
  }

  h2 {
    font-size: 20px;
    line-height: 30px;
  }

  h3 {
    font-size: 18px;
    line-height: 28px;
  }

  h4 {
    font-size: 16px;
    line-height: 26px;
  }

  h5 {
    font-size: 16px;
    line-height: 24px;
  }

  h6 {
    font-size: 16px;
    line-height: 24px;
  }

  /* 列表（有序，无序） */
  ul,
  ol {
    margin: 0 0 8px 0;
    padding: 0;
    line-height: 24px;
  }

  li {
    margin: 4px 0 0 20px;
    margin-bottom: 10px;
    word-break: break-all;
    word-wrap: break-word;
  }

  ol > li {
    list-style-type: decimal;
    margin-bottom: 10px;
    // 表达式,修复有序列表序号展示不全的问题
    // &:nth-child(n + 10) {
    //     margin-left: 30px;
    // }

    // &:nth-child(n + 100) {
    //     margin-left: 30px;
    // }
  }

  ul > li {
    list-style-type: disc;
    line-height: 24px;
    margin-right: 11px;
    margin-bottom: 10px;
  }

  ol ul,
  ol ul > li,
  ul ul,
  ul ul li {
    // list-style: circle;
    font-size: 16px;
    list-style: none;
    margin-left: 6px;
    margin-bottom: 1rem;
  }

  ul ul ul,
  ul ul ul li,
  ol ol,
  ol ol > li,
  ol ul ul,
  ol ul ul > li,
  ul ol,
  ul ol > li {
    list-style: square;
  }
}
</style>
